package homelesspartners.server;

import homelesspartners.client.model.City;
import homelesspartners.client.model.Gift;
import homelesspartners.client.model.Shelter;
import homelesspartners.client.model.Story;
import homelesspartners.client.model.ThankYou;
import homelesspartners.client.model.ThankYouPages;
import homelesspartners.client.model.User;
import homelesspartners.server.utils.ScriptRunner;
import homelesspartners.server.utils.SortOrderUpdater;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;

import javax.naming.NamingException;

import junit.framework.TestCase;
import ashbyutils.database.testing.JndiUnitTestHelper;

public class DataTest extends TestCase {

	private static final String[] EXPECTED_CITIES = new String[] { "Calgary", "New York", "Vancouver" };
	private static final String[][] EXPECTED_SHELTERS =
			new String[][] { new String[] { "Centre of Hope", "Drop-in Center", "Salvation Army" },
					new String[] { "Salvation Army" }, new String[0] };
	private static final String[][] EXPECTED_STORIES =
			new String[][] { new String[] { "Jill", "Jim", "Joe" }, new String[] { "Jenn" }, new String[0] };
	private static final String[] EXPECTED_GIFTS = new String[] { "Back Pack", "Jacket", "Socks" };
	private static final int[] EXPECTED_SHOPPING_LIST = new int[] { 2, 5, 8, 11 };
	private static final String[] EXPECTED_SHOPPING_LIST_NAMES = new String[] { "Joe", "Jill", "Jim", "Jenn" };
	private static final String[] EXPECTED_SHELTER_LIST_NAMES =
			new String[] { "Centre of Hope", "Centre of Hope", "Centre of Hope", "Salvation Army" };
	private static Data data = null;

	protected void setUp() throws Exception {
		super.setUp();
		if (JndiUnitTestHelper.notInitialized()) {
			JndiUnitTestHelper.init("src/homelesspartners/server/DatabaseUnitTests.properties");
		}
		ScriptRunner.runScriptOption(5);
		data = new Data();
	}

	protected void tearDown() throws Exception {
		super.tearDown();
		data = null;
	}

	public void testGetCities() throws DataException {

		ArrayList cities = data.getCities();
		int x = 0;
		for (Iterator citys = cities.iterator(); citys.hasNext(); x++) {
			City city = (City) citys.next();
			assertEquals("ITERATION: " + x, EXPECTED_CITIES[x], city.getName());
		}
	}

	public void testGetShelters() throws DataException {
		for (int y = 0; y < EXPECTED_SHELTERS.length; y++) {
			ArrayList<Shelter> shelters = data.getShelters(y + 1);
			assertEquals(EXPECTED_SHELTERS[y].length, shelters.size());
			for (int x = 0; x < shelters.size(); x++) {
				Shelter shelter = shelters.get(x);
				assertEquals("ITERATION: " + x, EXPECTED_SHELTERS[y][x], shelter.getName());
			}
		}

	}

	public void testGetStories() throws DataException {
		for (int y = 0; y < EXPECTED_STORIES.length; y++) {
			ArrayList<Story> stories = data.getStories(y + 1, null, null);
			assertEquals(EXPECTED_STORIES[y].length, stories.size());
			for (int x = 0; x < stories.size(); x++) {
				Story story = stories.get(x);
				assertEquals("ITERATION: " + x, EXPECTED_STORIES[y][x], story.getFirstName());
				Gift[] gifts = story.getGifts();
				assertEquals(EXPECTED_GIFTS.length, story.getGiftCount());
				for (int z = 0; z < story.getGiftCount(); z++) {
					Gift gift = gifts[z];
					assertEquals("ITERATION: " + z, EXPECTED_GIFTS[z], gift.getDescription());
					assertEquals("ITERATION: " + z, story.getId(), gift.getStory().getId());
					if (gift.getDescription().equals("Jacket")) {
						assertEquals(true, gift.isPledged());
					} else {
						assertEquals(false, gift.isPledged());
					}
				}
			}
		}
	}

	public void testRecordPledge() throws DataException {
		for (int y = 0; y < EXPECTED_STORIES.length; y++) {
			ArrayList<Story> stories = data.getStories(y + 1, null, null);
			for (int x = 0; x < stories.size(); x++) {
				Story story = stories.get(x);
				Gift[] gifts = story.getGifts();
				for (int z = 0; z < story.getGiftCount(); z++) {
					Gift gift = gifts[z];
					boolean result = data.recordPledge(gift.getId(), 1);
					if (gift.getDescription().equals("Jacket")) {
						assertFalse(result);
					} else {
						assertTrue(result);
					}
				}
			}
		}
	}

	public void testLogin() throws DataException {

		User user = data.login("ashbygreg@yahoo.com", "asdf");
		assertEquals(1, user.getUserId());
		assertEquals(User.ADMIN, user.getRole());
		user = data.login("none@hp.com", "qwer45");
		assertEquals(2, user.getUserId());
		assertEquals(User.NONE, user.getRole());
		user = data.login("typer@hp.com", "qwer45");
		assertEquals(3, user.getUserId());
		assertEquals(User.TYPIST, user.getRole());
		user = data.login("coord@hp.com", "qwer45");
		assertEquals(4, user.getUserId());
		assertEquals(User.COORDINATOR, user.getRole());

		// bad ids
		user = data.login("ashbygreg@yahoo.com", null);
		assertNull(user);
		user = data.login("ashbygreg@yahoo.com", "ASDF");
		assertNull(user);
		user = data.login("gjashby@hotmail.com", "qwerty");
		assertNull(user);
	}

	public void testCreateNewUser() throws DataException {

		try {
			data.createNewUser("ashbygreg@yahoo.com", "asdf");
			assertTrue(false);
		} catch (DuplicateDataException e) {
			// we're expecting this to already exist in the database
		}

		User user = data.createNewUser("asdf@asdf.com", "qwerty");
		assertEquals(5, user.getUserId());
	}

	public void testGetShoppingList() throws DataException {
		ArrayList gifts = data.getShoppingList(1);
		assertEquals(4, gifts.size());
		int x = 0;
		for (Iterator iter = gifts.iterator(); iter.hasNext(); x++) {
			Gift gift = (Gift) iter.next();
			assertEquals(EXPECTED_SHOPPING_LIST[x], gift.getId());
			assertEquals(EXPECTED_SHOPPING_LIST_NAMES[x], gift.getStory().getFirstName());
			assertEquals(EXPECTED_SHELTER_LIST_NAMES[x], gift.getStory().getShelter().getName());
			assertTrue(gift.getStory().getShelter().getStories().contains(gift.getStory()));
		}
	}

	public void testAddInterview() throws DataException {
		Story story = createTestStory();

		Story addedStory = data.addInterview(story);
		assertNotNull(addedStory);
		assertTrue(addedStory.getId() > 0);
		assertEquals(2, addedStory.getGiftCount());
		int story1Id = story.getId();

		Story addedStory2 = data.addInterview(story);
		assertNotNull(addedStory2);
		assertEquals(addedStory2.getId(), story1Id + 1);
		assertEquals(2, addedStory2.getGiftCount());

	}

	public void testAddShelter() throws DataException {
		Shelter shelter = createTestShelter();

		Shelter addedShelter = data.addShelter(shelter);
		assertNotNull(addedShelter);
		assertTrue(addedShelter.getId() > 0);
		int shelter1Id = shelter.getId();

		Shelter addedShelter2 = data.addShelter(shelter);
		assertNotNull(addedShelter2);
		assertEquals(addedShelter2.getId(), shelter1Id + 1);

		addedShelter = createTestShelter();
		addedShelter.setId(shelter1Id);
		ArrayList shelters = data.getShelters(1);
		assertTrue(shelters.contains(addedShelter));
		assertTrue(shelters.contains(addedShelter2));

	}

	private Shelter createTestShelter() {
		Shelter shelter = new Shelter();
		shelter.setAdditionalDetails("Here are some details");
		shelter.setAddress("some address");
		shelter.setCityId(1);
		shelter.setName("a shelter name");
		shelter.setPhone("123 123 1324");
		shelter.setIdFormat("I'm sure glad I have these tests");
		shelter.setWhatTheyDo("this is what they do");
		shelter.setWhatTheyNeed("this is what they need");
		return shelter;
	}

	private Story createTestStory() {
		Story story = new Story();
		story.setAssignedId("1234");
		story.setFirstName("Test");
		story.setGender('F');
		story.setLastInitial("T");
		story.setShelter(new Shelter());
		story.getShelter().setId(1);
		story.setStory("this is the story details");
		Gift gift = new Gift();
		gift.setId(-1);
		gift.setDescription("something");
		story.addGift(gift);
		Gift gift2 = new Gift();
		gift2.setId(-2);
		gift2.setDescription("something else");
		story.addGift(gift2);
		return story;
	}

	public void testDeleteStory() throws DataException {

		try {
			data.deleteStory(1);
			fail("This should not have passed because it has pledged gifts");
		} catch (Exception e) {
			// this should fail!
		}
		assertTrue(data.removePledge(2, 1));
		assertTrue(data.deleteStory(1));
	}

	public void testUpdateStory() throws DataException {

		// create a test story
		// add to database
		// update all the fields
		// do a find and see if all the fields are correct
		// add two gifts
		// do a find and see if all the gifts are correct
		// delete the first 2 gifts
		// do a find and see if all the gifts are correct

		Story story = createTestStory();
		data.addInterview(story);
		final String assignedId = "789";
		story.setAssignedId(assignedId);
		final String firstName = "Changed";
		story.setFirstName(firstName);
		final char gender = 'F';
		story.setGender(gender);
		final String lastInitial = "C";
		story.setLastInitial(lastInitial);
		final String storyDetail = "This is new";
		story.setStory(storyDetail);
		final String gift1Desc = "New GIFT1";
		story.getGifts()[0].setDescription(gift1Desc);
		final String gift2Desc = "New GIFT2";
		story.getGifts()[1].setDescription(gift2Desc);
		Story updated = data.updateStory(story);

		assertEquals(updated, story); // ie. they are equal... the id didn't
		// change

		assertEquals(assignedId, updated.getAssignedId());
		assertEquals(firstName, updated.getFirstName());
		assertEquals(gender, updated.getGender());
		assertEquals(lastInitial, updated.getLastInitial());
		assertEquals(storyDetail, updated.getStory());
		assertEquals(gift1Desc, updated.getGifts()[0].getDescription());
		assertEquals(gift2Desc, updated.getGifts()[1].getDescription());

		Gift newGift1 = new Gift();
		newGift1.setId(-1);
		newGift1.setDescription(gift1Desc);

		Gift newGift2 = new Gift();
		newGift2.setId(-2);
		newGift2.setDescription(gift1Desc);

		updated.addGift(newGift1);
		updated.addGift(newGift2);

		story = data.updateStory(updated);
		assertEquals(4, story.getGiftCount());

	}

	public void testUpdateShelter() throws DataException {
		// create a test story
		// add to database
		// update all the fields
		// do a find and see if all the fields are correct
		Shelter shelter = createTestShelter();
		Shelter addedShelter = data.addShelter(shelter);
		shelter.setId(addedShelter.getId());
		shelter.setAdditionalDetails("Here are some detailsz");
		shelter.setAddress("some addressz");
		shelter.setName("a shelter namez");
		shelter.setPhone("123 123 1324z");
		shelter.setWhatTheyDo("this is what they doz");
		shelter.setWhatTheyNeed("this is what they needz");
		data.updateShelter(shelter);

		ArrayList shelters = data.getShelters(1);
		Shelter updatedShelter = (Shelter) shelters.get(shelters.indexOf(shelter));
		assertEquals(shelter.getAdditionalDetails(), updatedShelter.getAdditionalDetails());
		assertEquals(shelter.getAddress(), updatedShelter.getAddress());
		assertEquals(shelter.getCityId(), updatedShelter.getCityId());
		assertEquals(shelter.getName(), updatedShelter.getName());
		assertEquals(shelter.getPhone(), updatedShelter.getPhone());
		assertEquals(shelter.getWhatTheyDo(), updatedShelter.getWhatTheyDo());
		assertEquals(shelter.getWhatTheyNeed(), updatedShelter.getWhatTheyNeed());

	}

	public void testDeleteShelter() throws DataException {
		Shelter shelter = createTestShelter();
		Shelter addedShelter = data.addShelter(shelter);
		Story story = createTestStory();
		story.setShelter(addedShelter);
		Story addedStory = data.addInterview(story);

		try {
			data.deleteShelter(addedShelter.getId());
			fail("this should have failed because the shelter has a story");
		} catch (DataException e) {
			// do nothing, this was expected
		}

		data.deleteStory(addedStory.getId());
		assertTrue(data.deleteShelter(addedShelter.getId()));
	}

	public void testCaseInsensitiveUserIds() throws DataException {
		User newUser = data.createNewUser("mYaDdReSs@a.a", "qwer45");
		User loggedInUser = data.login("MyAdDrEsS@a.a", "qwer45");
		assertEquals(newUser.getUserId(), loggedInUser.getUserId());
	}

	public void testUpdateVolunteer() throws DataException {
		User user = new User();
		user.setRole(2);

		// test a non-existent user
		user.setEmailAddress("test@test.test");
		try {
			data.updateVolunteer(user);
			fail("this should have failed because the user doesn't exist, so a password is required");
		} catch (DataException e) {
			// expected
		}

		user.setPassword("asd");
		try {
			data.updateVolunteer(user);
			fail("this should have failed because the password is too short");
		} catch (DataException e) {
			// expected
		}

		user.setPassword("asdf");
		user.setEmailAddress("test@test.");
		try {
			data.updateVolunteer(user);
			fail("this should have failed because the userid is incorrect");
		} catch (DataException e) {
			// expected
		}

		// test an existent user
		user.setEmailAddress("test@test.test");
		data.createNewUser(user.getEmailAddress(), user.getPassword());
		assertTrue(data.updateVolunteer(user).booleanValue());

		user.setRole(1);
		data.updateVolunteer(user);
		User test = data.login("test@test.test", "asdf");
		assertEquals(1, test.getRole());
	}

	public void testGetStoryPage() throws DataException, IOException, NamingException, SQLException {
		System.out.println("********** Starting GetStoryPage **********");

		// load additional data
		// get first page
		// get second page, validate none of the second page are in the first
		// keep getting more until less than a full page is returned
		// confirm the last one has fewer gifts than the first

		ScriptRunner.runScriptOption(6);
		SortOrderUpdater.main(new String[0]);
		ArrayList<Story> page0 = data.getPageOfStories(1, 0);
		ArrayList<Story> page1 = data.getPageOfStories(1, 1 * Shelter.PAGE_SIZE);
		for (Iterator storyIter = page0.iterator(); storyIter.hasNext();) {
			Story story = (Story) storyIter.next();
			System.out.println(story.getId());
		}
		for (Iterator storyIter = page1.iterator(); storyIter.hasNext();) {
			Story story = (Story) storyIter.next();
			System.out.println(story.getId());
			assertFalse(page0.contains(story));
		}
		ArrayList<Story> pageLast = data.getPageOfStories(1, 2 * Shelter.PAGE_SIZE);
		int pageNum = 3;
		while (pageLast.size() == Shelter.PAGE_SIZE) {
			pageLast = data.getPageOfStories(1, pageNum * Shelter.PAGE_SIZE);
			for (Iterator storyIter = pageLast.iterator(); storyIter.hasNext();) {
				Story story = (Story) storyIter.next();
				System.out.println(story.getId());
			}
			pageNum++;

		}
		int storiesRetrieved = (pageNum - 1) * Shelter.PAGE_SIZE;
		storiesRetrieved += pageLast.size();

		assertEquals(103, storiesRetrieved);
		int firstStoryUnpledgedGifts = countUnpledgedGifts(page0.get(0));
		int lastStoryUnpledgedGifts = countUnpledgedGifts(pageLast.get(pageLast.size() - 1));
		System.out.println(lastStoryUnpledgedGifts);
		System.out.println(firstStoryUnpledgedGifts);
		assertTrue(lastStoryUnpledgedGifts < firstStoryUnpledgedGifts);

		data.recordPledge(page0.get(0).getGifts()[0].getId(), 1);
		data.recordPledge(page0.get(0).getGifts()[1].getId(), 1);
		SortOrderUpdater.main(new String[0]);
		ArrayList<Story> page0Again = data.getPageOfStories(1, 0);
		assertFalse(page0Again.contains(page0.get(0)));
		ArrayList<Story> pageLastAgain = data.getPageOfStories(1, (pageNum - 1) * Shelter.PAGE_SIZE);
		assertTrue(pageLastAgain.contains(page0.get(0)));

		System.out.println("********** Ending GetStoryPage **********");
	}

	private int countUnpledgedGifts(Story story) {
		Gift[] gifts = story.getGifts();
		int unpledgedCount = 0;
		for (int x = 0; x < gifts.length; x++) {
			Gift gift = gifts[x];
			if (gift != null && !gift.isPledged()) {
				unpledgedCount++;
			}
		}
		return unpledgedCount;
	}

	/**
	 * REQUIREMENTS: TODO figure out requirements and how to test this
	 * 
	 * @throws SQLException
	 * @throws NamingException
	 * @throws IOException
	 * @throws DataException
	 */
	public void testGetThankYouNotes() throws IOException, NamingException, SQLException, DataException {
		System.out.println("********** Starting ThankYouNote **********");

		ScriptRunner.runScriptOption(6);
		ScriptRunner.runScriptOption(7);

		SortOrderUpdater.main(new String[0]);
		ArrayList<ThankYou> page0 = data.getPageOfThankYous(0);
		ArrayList<ThankYou> page1 = data.getPageOfThankYous(1 * ThankYouPages.PAGE_SIZE);

		for (Iterator thankyouIter = page0.iterator(); thankyouIter.hasNext();) {
			ThankYou thankYou = (ThankYou) thankyouIter.next();
			System.out.println(thankYou.toString());
		}
		for (Iterator thankyouIter = page1.iterator(); thankyouIter.hasNext();) {
			ThankYou thankyou = (ThankYou) thankyouIter.next();
			System.out.println(thankyou);
			assertFalse(page0.contains(thankyou));
		}
		ArrayList<ThankYou> pageLast = data.getPageOfThankYous(0);
		int pageNum = 0;
		while (pageLast.size() == ThankYouPages.PAGE_SIZE) {
			pageLast = data.getPageOfThankYous(pageNum * ThankYouPages.PAGE_SIZE);
			pageNum++;

		}

		int thankyousRetrieved = (pageNum - 1) * ThankYouPages.PAGE_SIZE;
		thankyousRetrieved += pageLast.size();
		assertEquals(119, thankyousRetrieved);

		System.out.println("********** Ending ThankYouNote **********");

	}

	/**
	 * add a story add the same story -- fails change the casing and add --
	 * fails change the shelter - pass change shelter back and: - change each of
	 * name, initial, assigned id - duplicate warning - change 2 of each of
	 * name, initial, assigned id - passes
	 * 
	 * @throws DataException
	 */
	public void testDuplicateStories() throws DataException {
		Shelter shelter = new Shelter();
		shelter.setId(1);
		Story story = new Story();
		story.setShelter(shelter);
		story.setFirstName("Mike");
		story.setLastInitial("W");
		story.setAssignedId("123a");
		story.setGender('M');
		story.setStory("This is the story of Mike W.");

		ArrayList returnedStories = data.addInterviewCheckFirst(story);
		assertEquals(1, returnedStories.size());
		assertTrue(((Story) returnedStories.get(0)).getId() > 0);

		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() > 1);

		story.setFirstName("mike");
		story.setLastInitial("w");
		story.setAssignedId("123A");

		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() > 1);

		shelter.setId(2);
		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() == 1);

		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() > 1);

		story.setFirstName("george");
		story.setLastInitial("w");
		story.setAssignedId("123a");
		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() > 1);

		story.setFirstName("mike");
		story.setLastInitial("q");
		story.setAssignedId("123a");
		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() > 1);

		story.setFirstName("mike");
		story.setLastInitial("w");
		story.setAssignedId("123b");
		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() > 1);

		story.setFirstName("mike");
		story.setLastInitial("q");
		story.setAssignedId("123b");
		returnedStories = data.addInterviewCheckFirst(story);
		assertTrue(returnedStories.size() == 1);

	}
}
